
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.*;

/**
 * Clase para representar al datapath de una manera gráfica
 *
 * @author Villalva Bueno Arely Eunice
 * @author Romero Rodriguez Gabriel Eduardo
 * @see ListBoxes
 * @see Stages
 * @see UI
 * @see Instruction
 * @see Datapath
 * @see Stage
 * @see Fetch
 * @see Decode
 * @see Execute
 * @see Memory
 * @see WriteBack
 * @see MemoryType
 * @see Help
 */
public class GUI extends java.applet.Applet implements ActionListener {

    UI usr = new UI();
    public Stages stg = new Stages();
    public ListBoxes lb = new ListBoxes();
    public Help theHelp;
    public static Vector instr = new Vector();
    public static Datapath sim;
    private boolean stepped, instrAdded;
    public int numCycles = 0;
    public double CPI = 0.0;
    public static JButton qButton = new JButton("Quitar");

    public static void main(String[] args) {
        GUI theGUI = new GUI();

        JFrame win = new JFrame("Datapath");
        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints constraints = new GridBagConstraints();
        win.setLayout(gridbag);

        theGUI.buildConstraints(constraints, 0, 0, 1, 1, 100, 90);
        constraints.fill = GridBagConstraints.BOTH;
        constraints.anchor = GridBagConstraints.CENTER;
        gridbag.setConstraints(theGUI, constraints);
        win.add(theGUI);

        qButton.addActionListener(theGUI);

        theGUI.buildConstraints(constraints, 0, 1, 1, 1, 100, 10);
        constraints.fill = GridBagConstraints.NONE;
        constraints.anchor = GridBagConstraints.CENTER;
        gridbag.setConstraints(qButton, constraints);
        win.add(qButton);

        theGUI.init();
        win.pack();
        win.setSize(new Dimension(920, 620));
        win.setVisible(true);

        theGUI.start();
    }

    void buildConstraints(GridBagConstraints gbc, int gx, int gy,
            int gw, int gh, int wx, int wy) {
        gbc.gridx = gx;
        gbc.gridy = gy;
        gbc.gridwidth = gw;
        gbc.gridheight = gh;
        gbc.weightx = wx;
        gbc.weighty = wy;
    }

    public void init() {
        sim = new Datapath(instr, lb, stg);
      
        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints constraints = new GridBagConstraints();
        setLayout(gridbag);

    
        buildConstraints(constraints, 0, 0, 1, 1, 100, 50);
        constraints.fill = GridBagConstraints.BOTH;
        constraints.anchor = GridBagConstraints.CENTER;
        gridbag.setConstraints(lb, constraints);
        lb.Instructions.addActionListener(this);
        add(lb);

      
        buildConstraints(constraints, 0, 1, 1, 1, 100, 30);
        constraints.fill = GridBagConstraints.BOTH;
        constraints.anchor = GridBagConstraints.CENTER;
        gridbag.setConstraints(stg, constraints);
        add(stg);

    
        buildConstraints(constraints, 0, 2, 1, 1, 100, 20);
        constraints.fill = GridBagConstraints.BOTH;
        constraints.anchor = GridBagConstraints.CENTER;
        gridbag.setConstraints(usr, constraints);
        usr.tfai.addActionListener(this);
        usr.addinst.addActionListener(this);
        usr.tfrun.addActionListener(this);
        usr.run.addActionListener(this);
        usr.step.addActionListener(this);
        usr.reset.addActionListener(this);
        usr.clear.addActionListener(this);
        usr.delete.addActionListener(this);
        usr.edit.addActionListener(this);
        usr.tfedit.addActionListener(this);
        usr.help.addActionListener(this);
        add(usr);

        usr.step.setEnabled(false);
        usr.run.setEnabled(false);
        usr.tfrun.setEnabled(false);

        validate();

        stepped = false;
        instrAdded = false;

        theHelp = new Help(this);
    }

    public Insets getInsets() {
        return new Insets(4, 5, 4, 5);
    }

    /**
     * Método para manejar los distintos eventos de los componentes gráficos
     * 
     * @see UI
     */
    public void actionPerformed(ActionEvent e) {
        String temp;
        int tempnum, j = 0;
        Instruction tempinstr;
        boolean OK, done;

        Object src = e.getSource();

        if ((src == usr.addinst) || (src == usr.tfai)) {
            temp = usr.tfai.getText();
            temp = temp.toUpperCase();
            tempinstr = new Instruction(temp);
            if (tempinstr.valid() == true) {
                if (!instrAdded) {
                    usr.step.setEnabled(true);
                    usr.run.setEnabled(true);
                    usr.tfrun.setEnabled(true);
                    instrAdded = true;
                }
                instr.addElement(tempinstr);
                tempnum = lb.Instructions.getItemCount();
                temp = tempnum + ": " + temp;
                lb.Instructions.add(temp);
                usr.tfai.selectAll();
                usr.tfai.requestFocus();
            } else {
                lb.Messages.add(tempinstr.elError(), 0);
            }

        } else if ((src == usr.reset) || (src == usr.clear)) {
            lb.Messages.removeAll();
            lb.initRF();
            lb.initMemory();
            stg.nCycles.setText("0");
            stg.nInstr.setText("0");
            stg.cpi.setText("0.0");
            numCycles = 0;
            stg.clearAll();
            stepped = false;
            setEnabledUI(true);
            if (src == usr.reset) {
                lb.Messages.add("El programa ha sido reseteado.", 0);
                if (instrAdded) {
                    usr.step.setEnabled(true);
                    usr.run.setEnabled(true);
                    usr.tfrun.setEnabled(true);
                }
            } else {
                lb.Messages.add("El Programa ha sido limpiado.", 0);
                if (!instr.isEmpty()) {
                    instr.removeAllElements();
                    lb.Instructions.removeAll();
                }
                instrAdded = false;
                usr.step.setEnabled(false);
                usr.run.setEnabled(false);
                usr.tfrun.setEnabled(false);
            }
            sim = new Datapath(instr, lb, stg);
        } else if ((src == usr.step) || (src == usr.run) || (src == usr.tfrun)) {
            usr.step.setEnabled(false);
            usr.run.setEnabled(false);
            usr.tfrun.setEnabled(false);
            usr.reset.setEnabled(false);
            usr.clear.setEnabled(false);
            usr.help.setEnabled(false);

            done = false;

            if (!stepped) {
                sim.enableForwarding(usr.doForwarding());
                setEnabledUI(false);
                stepped = true;
            }

            if (src == usr.step) {
                done = sim.step();
            } else {
                OK = true;
                try {
                    j = Integer.parseInt(usr.tfrun.getText());
                } catch (NumberFormatException exc) {
                    lb.Messages.add("La longitud run es inválida.", 0);
                    OK = false;
                }
                RUNLOOP:
                if (OK) {
                    if (j > 0) {
                        if (!stepped) {
                            sim.enableForwarding(usr.doForwarding());
                            sim.enableBP(usr.doBP());
                            setEnabledUI(false);
                            stepped = true;
                        }
                        for (tempnum = 0; tempnum < j; tempnum++) {
                            done = sim.step();
                            numCycles++;
                            if (done) {
                                break RUNLOOP;
                            }
                        }
                        temp = "terminado " + j + " etapas.";
                        lb.Messages.add(temp, 0);
                    } else {
                        lb.Messages.add("La longitud es inválida", 0);
                    }
                }
            }

            numCycles++;
            stg.nCycles.setText(String.valueOf(numCycles));
            j = sim.myWriteBack.getNumeroDeInstruc();
            stg.nInstr.setText(String.valueOf(j));
            if (j > 0) {
                CPI = (float) numCycles / (float) j;
            }
            stg.cpi.setText(String.valueOf(CPI));

            if (done) {
                usr.step.setEnabled(false);
                usr.run.setEnabled(false);
                usr.tfrun.setEnabled(false);
            } else {
                usr.step.setEnabled(true);
                usr.run.setEnabled(true);
                usr.tfrun.setEnabled(true);
            }
            usr.reset.setEnabled(true);
            usr.clear.setEnabled(true);
            usr.help.setEnabled(true);

        } else if (src == usr.delete) {
            tempnum = lb.Instructions.getSelectedIndex();
            if (tempnum != -1) {
                instr.removeElementAt(tempnum);
                lb.Instructions.removeAll();
                for (tempnum = 0; tempnum < instr.size(); tempnum++) {
                    tempinstr = (Instruction) instr.elementAt(tempnum);
                    temp = tempnum + ": " +
                            tempinstr.toString();
                    lb.Instructions.add(temp);
                }
            } else {
                lb.Messages.add("Una instruccion debe ser seleccionada", 0);
            }

        } else if ((src == usr.edit) || (src == usr.tfedit)) {
            tempnum = lb.Instructions.getSelectedIndex();
            if (tempnum != -1) {
                temp = usr.tfedit.getText();
                temp = temp.toUpperCase();
                tempinstr = new Instruction(temp);
                if (tempinstr.valid() == true) {
                    instr.setElementAt(tempinstr, tempnum);
                    temp = tempnum + ": " + temp;
                    lb.Instructions.replaceItem(temp, tempnum);
                    usr.tfedit.selectAll();
                    usr.tfedit.requestFocus();
                } else {
                    lb.Messages.add(tempinstr.elError(), 0);
                }
            } else {
                lb.Messages.add("Una instrucion debe ser selecionada", 0);
            }
        } else if (src == usr.help) {
            theHelp.show();
            theHelp.toFront();
        } else if (src == qButton) {
            System.exit(0);
        }

    }

    /**
     * Fija la condición de los componentesde la interfaz.
     * 
     * @see UI
     */
    void setEnabledUI(boolean cond) {
        usr.addinst.setEnabled(cond);
        usr.tfai.setEnabled(cond);
        usr.delete.setEnabled(cond);
        usr.edit.setEnabled(cond);
        usr.branch.setEnabled(cond);
        usr.forward.setEnabled(cond);
        usr.tfedit.setEnabled(cond);
    }
}
